﻿using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Yz.Base;
using Yz.Base.Constants;
using Yz.Cache.Base;
using Yz.Core.DBUtility;
using Yz.Core.Extensions;
using Yz.Model.Config;

namespace Yz.Cache
{
    public abstract class BaseCacheUitility
    {
        public IOptions<CacheConfig> _optionCacheConfig { get; set; }
        protected CacheConfig _cacheConfig => _optionCacheConfig.Value;
        #region 从缓存中获取数据（有委托）

        #region 没有参数

        #endregion

        #region 1个参数

        #endregion

        #region 2个参数
        /// <summary>
        /// 从缓存中获取数据或更新缓存 
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="func">委托方法</param>
        /// <param name="args">至少有2个参数（从右往左（从后往前），1：是否去更新缓存，2：CacheDbTime 对象（包含时间和数据库索引） </param>
        /// <returns>List<T></returns>
        public T GetFromCache<TEntity, T>(Func<Expression<Func<TEntity, bool>>, List<ParaOrder<TEntity>>, T> func, params object[] args) where TEntity : BaseEntity where T : ViewModel
        {
            return GetDataFromCache<T>(func, args);
        }
        /// <summary>
        /// 从缓存中获取数据或更新缓存 
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="func">委托方法</param>
        /// <param name="args">至少有2个参数（从右往左（从后往前），1：是否去更新缓存，2：CacheDbTime 对象（包含时间和数据库索引） </param>
        /// <returns>List<T></returns>
        public async Task<T> GetFromCacheAsync<TEntity, T>(Func<Expression<Func<TEntity, bool>>, List<ParaOrder<TEntity>>, Task<T>> func, params object[] args) where TEntity : BaseEntity where T : ViewModel
        {
            return await GetDataFromCacheAsync<T>(func, args);
        }
        #endregion

        #region 3个参数

        #endregion

        #region 4个参数
        /// <summary>
        /// 从缓存中获取数据或更新缓存（string,int,int）
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="func">委托方法</param>
        /// <param name="args">至少有2个参数（从右往左（从后往前），1：是否去更新缓存，2：CacheDbTime 对象（包含时间和数据库索引）</param>
        /// <returns>List<T></returns>
        public ReturnList<T> GetListFromCache<TEntity, T>(Func<Expression<Func<TEntity, bool>>, ParaPage, List<ParaOrder<TEntity>>, bool, ReturnList<T>> Func, params object[] args) where TEntity : BaseEntity where T : ViewModel
        {
            return GetDataListFromCache<T>(Func, args);
        }
        /// <summary>
        /// 从缓存中获取数据或更新缓存（string,int,int）
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="func">委托方法</param>
        /// <param name="args">至少有2个参数（从右往左（从后往前），1：是否去更新缓存，2：CacheDbTime 对象（包含时间和数据库索引）</param>
        /// <returns>List<T></returns>
        public async Task<ReturnList<T>> GetListFromCacheAsync<TEntity, T>(Func<Expression<Func<TEntity, bool>>, ParaPage, List<ParaOrder<TEntity>>, bool, Task<ReturnList<T>>> Func, params object[] args) where TEntity : BaseEntity where T : ViewModel
        {
            return await GetDataListFromCacheAsync<T>(Func, args);
        }
        /// <summary>
        /// 从缓存中获取数据或更新缓存 
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="func">委托方法</param>
        /// <param name="args">至少有2个参数（从右往左（从后往前），1：是否去更新缓存，2：CacheDbTime 对象（包含时间和数据库索引）</param>
        /// <returns>List<T></returns>
        public ReturnList<T> GetListFromCache<TEntity, T>(Func<List<Expression<Func<TEntity, bool>>>, ParaPage, List<ParaOrder<TEntity>>, bool, ReturnList<T>> Func, params object[] args) where TEntity : BaseEntity where T : ViewModel
        {
            return GetDataListFromCache<T>(Func, args);
        }
        /// <summary>
        /// 从缓存中获取数据或更新缓存 
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="func">委托方法</param>
        /// <param name="args">至少有2个参数（从右往左（从后往前），1：是否去更新缓存，2：CacheDbTime 对象（包含时间和数据库索引）</param>
        /// <returns>List<T></returns>
        public async Task<ReturnList<T>> GetListFromCacheAsync<TEntity, T>(Func<List<Expression<Func<TEntity, bool>>>, ParaPage, List<ParaOrder<TEntity>>, bool, Task<ReturnList<T>>> Func, params object[] args) where TEntity : BaseEntity where T : ViewModel
        {
            return await GetDataListFromCacheAsync<T>(Func, args);
        }
        #endregion

        #region 5个参数
        #endregion

        #region 6个参数
        #endregion

        #region 7个参数
        #endregion

        #region 8个参数
        #endregion

        #endregion

        #region 具体执行缓存或者更新换存方法
        /// <summary>
        /// 具体执行缓存委托或更新缓存
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="func">委托方法</param>
        /// <param name="args">至少有2个参数（从右往左（从后往前），1：是否去更新缓存，2：CacheDbTime 对象（包含时间和数据库索引）</param>
        /// <returns>List<T></returns>
        protected abstract T GetDataFromCache<T>(Delegate func, object[] args) where T : ViewModel;
        /// <summary>
        /// 具体执行缓存委托或更新缓存
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="func">委托方法</param>
        /// <param name="args">至少有2个参数（从右往左（从后往前），1：是否去更新缓存，2：CacheDbTime 对象（包含时间和数据库索引）</param>
        /// <returns>List<T></returns>
        protected abstract Task<T> GetDataFromCacheAsync<T>(Delegate func, object[] args) where T : ViewModel;
        /// <summary>
        /// 具体执行缓存委托或更新缓存
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="func">委托方法</param>
        /// <param name="args">至少有2个参数（从右往左（从后往前），1：是否去更新缓存，2：CacheDbTime 对象（包含时间和数据库索引）</param>
        /// <returns>List<T></returns>
        protected abstract ReturnList<T> GetDataListFromCache<T>(Delegate func, object[] args) where T : ViewModel;

        /// <summary>
        /// 具体执行缓存委托或更新缓存
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="func">委托方法</param>
        /// <param name="args">至少有2个参数（从右往左（从后往前），1：是否去更新缓存，2：CacheDbTime 对象（包含时间和数据库索引）</param>
        /// <returns>List<T></returns>
        protected abstract Task<ReturnList<T>> GetDataListFromCacheAsync<T>(Delegate func, object[] args) where T : ViewModel;
        private int GetMinute()
        {
            Random ran = new Random();
            int RandKey = ran.Next(1, 10);
            return RandKey;
        }

        protected ParamCache GetCachePara(Delegate func, object[] args)
        {
            string fullName = func.Target.ToString();
            string funcName = fullName.Substring(fullName.LastIndexOf(".") + 1) + CacheConstant.Separator + func.Method.Name;
            List<object> keyParas = new List<object>();
            foreach (var item in args.Take(args.Length - 2).ToList())
            {
                if (item is LambdaExpression)
                {
                    Dictionary<string, string> dics = new Dictionary<string, string>();
                    LambdaExpression lambda = item as LambdaExpression;
                    var resolveLambda = new ResolveExpression(lambda);
                    dics = resolveLambda.Argument;
                    keyParas.Add(dics);
                }
                else if (item is List<LambdaExpression>)
                {
                    Dictionary<string, string> dics = new Dictionary<string, string>();
                    List<LambdaExpression> lambdaList = item as List<LambdaExpression>;
                    foreach (var lambda in lambdaList)
                    {
                        var resolveLambda = new ResolveExpression(lambda);
                        foreach (var item1 in resolveLambda.Argument)
                        {
                            dics.Add(item1.Key, item1.Value);
                        }
                    }
                    keyParas.Add(dics);
                }
                else
                {
                    keyParas.Add(item);
                }
            }
            string key = JsonConvert.SerializeObject(keyParas).GetHashCode().ToString();
            bool toUpdate = (bool)args[args.Length - 1];//是否是更新缓存
            var cacheDbTime = (CacheDbTime)args[args.Length - 2];//CacheDbTime 对象 
            string cacheKey = CacheConstant.Separator + funcName + key;//最终的缓存key值 
            DateTime dtTarget = DateTime.Now.AddHours(2);
            DateTimeOffset absoluteExpiration = DateTime.Now.AddHours(2).AddMinutes(GetMinute());
            switch (cacheDbTime.TimeType)
            {
                case TimeType.Hour:
                    dtTarget = DateTime.Now.AddHours(cacheDbTime.Time);
                    absoluteExpiration = DateTime.Now.AddHours(cacheDbTime.Time);
                    break;
                case TimeType.Day:
                    dtTarget = DateTime.Now.AddDays(cacheDbTime.Time);
                    absoluteExpiration = DateTime.Now.AddDays(cacheDbTime.Time);
                    break;
                default:
                    dtTarget = DateTime.Now.AddMinutes(cacheDbTime.Time);
                    absoluteExpiration = DateTime.Now.AddMinutes(cacheDbTime.Time);
                    break;
            }
            TimeSpan expiry = dtTarget.AddMinutes(GetMinute()) - DateTime.Now;
            absoluteExpiration = absoluteExpiration.AddMinutes(GetMinute());
            return new ParamCache()
            {
                DbNum = cacheDbTime.DbNum,
                Expiry = expiry,
                Key = cacheKey,
                Update = toUpdate,
                Expiration = absoluteExpiration
            };
        }
        #endregion


        protected string GetCacheKey(string cacheKey)
        {
            return _cacheConfig.CachePrefixName + cacheKey;
        }
    }
}
